﻿using System;
using System.Collections;
using AMC.SubstitutionCache.Services;
using Orchard.ContentManagement;
using Orchard.DisplayManagement;
using Orchard.DisplayManagement.Shapes;

namespace AMC.SubstitutionCache.Rendering
{
    public class ShapeHelper
    {
        private const int MaxDepth = 10;

        public static string GetShapeTypeForTemplate(string templateType, string templateName)
        {
            return templateType + ":" + templateName;
        }

        public static string GetShapeType(string shapeType, string templateName)
        {
            if (shapeType == "EditorTemplate" || shapeType == "DisplayTemplate")
            {
                shapeType = GetShapeTypeForTemplate(shapeType, templateName);
            }

            return shapeType;
        }

        public static string GetPartTag(ContentPart part)
        {
            return GetPartTag(part.GetType(), part.Id);
        }

        public static string GetPartTag<TContent>(int id)
        {
            return GetPartTag(typeof(TContent), id);
        }

        private static string GetPartTag(Type partType, int id)
        {
            if (id <= 0)
                return string.Empty;

            return "SubstitutionCache:" + partType.Name + ":" + id;
        }

        public static Shape FindShape(Shape shape, SubstitutionDriverRenderingInfo renderInfo, SubstitutionContext context, Shape layout, int depth = 0)
        {
            var locator = renderInfo.ShapeLocator ?? SubstitutionDriverRenderingInfo.DefaultLocator;

            var foundShape = FindShapeImplementation(locator, shape, context) ?? FindShapeImplementation(locator, layout, context);

            return foundShape;
        }

        private static Shape FindShapeImplementation(
            Func<dynamic, SubstitutionContext, bool> locator,
            Shape shape,
            SubstitutionContext context,
            int depth = 0)
        {
            if (shape == null)
                return null;

            // Are you the One?
            if (locator(shape, context))
                return shape; // there is no spoon!

            // avoid crush depth
            if (depth >= MaxDepth)
                return null;

            // each shape is dynamic and child shapes are all over the place
            // so, look through its properties, which is a HybridDictionary
            // and find shapes or list of shapes we haven't seen yet
            foreach (DictionaryEntry property in shape.Properties)
            {
                var zone = property.Value as Shape;

                if (zone == null)
                    continue;

                foreach (Shape child in ((dynamic)zone).Items)
                {
                    if (locator(child, context))
                        return child;
                }
            }

            return null;
        }
    }
}